#include "stm32f4xx.h"

#include "usbd_cdc_if.h"
#include "usbd_desc.h"
#include "log.h"

#define TAG "WINUSB"

uint8_t *UserRxBufferFS;
uint8_t *UserTxBufferFS;
USBD_HandleTypeDef *hUsbDeviceFS;

static int8_t CDC_Init_FS(void);
static int8_t CDC_DeInit_FS(void);
static int8_t CDC_Dev_Status(CDC_DEV_STATUS status);
static int8_t CDC_Control_FS(uint8_t cmd, uint8_t *pbuf, uint16_t length);
static int8_t CDC_Receive_FS(uint8_t *pbuf, uint32_t *Len, uint8_t epnum);

USBD_CDC_ItfTypeDef USBD_Interface_fops_FS =
    {
        CDC_Init_FS,
        CDC_DeInit_FS,
        CDC_Dev_Status,
        CDC_Control_FS,
        CDC_Receive_FS};

void USBD_CDC_DEV_INIT(void)
{
    USBD_StatusTypeDef res;
    hUsbDeviceFS = mymalloc(SRAMIN, sizeof(USBD_HandleTypeDef));
    if (hUsbDeviceFS == NULL)
    {
        ERR("malloc failed");
        goto err;
    }
    UserRxBufferFS = mymalloc(SRAMIN, sizeof(USBD_HandleTypeDef));
    if (UserRxBufferFS == NULL)
    {
        ERR("malloc failed");
        goto err;
    }
    UserTxBufferFS = mymalloc(SRAMIN, sizeof(USBD_HandleTypeDef));
    if (UserTxBufferFS == NULL)
    {
        ERR("malloc failed");
    }
    USB_Reset();
    res = MX_USB_DEVICE_Init(hUsbDeviceFS, &USBD_Interface_fops_FS); /* USB设备初始化 */
    if (res != USBD_OK)
    {
        ERR("USB Init failed");
        goto err;
    }
    INFO("USB Connecting...");
    return;
err:
    myfree(SRAMIN, hUsbDeviceFS);
    hUsbDeviceFS = NULL;
    myfree(SRAMIN, UserRxBufferFS);
    UserRxBufferFS = NULL;
    myfree(SRAMIN, UserTxBufferFS);
    UserTxBufferFS = NULL;
    return;
}
/* Private functions ---------------------------------------------------------*/
/**
 * @brief  Initializes the CDC media low layer over the FS USB IP
 * @retval USBD_OK if all operations are OK else USBD_FAIL
 */
static int8_t CDC_Init_FS(void)
{
    /* USER CODE BEGIN 3 */
    /* Set Application Buffers */
    USBD_CDC_SetTxBuffer(hUsbDeviceFS, UserTxBufferFS, 0);
    USBD_CDC_SetRxBuffer(hUsbDeviceFS, UserRxBufferFS);
    return (USBD_OK);
    /* USER CODE END 3 */
}

/**
 * @brief  DeInitializes the CDC media low layer
 * @retval USBD_OK if all operations are OK else USBD_FAIL
 */
static int8_t CDC_DeInit_FS(void)
{
    /* USER CODE BEGIN 4 */
    return (USBD_OK);
    /* USER CODE END 4 */
}

int8_t CDC_Dev_Status(CDC_DEV_STATUS status)
{
    INFO("CDC_Dev_Status:%d", status);
    return 0;
}

/**
 * @brief  Manage the CDC class requests
 * @param  cmd: Command code
 * @param  pbuf: Buffer containing command data (request parameters)
 * @param  length: Number of data to be sent (in bytes)
 * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
 */
static int8_t CDC_Control_FS(uint8_t cmd, uint8_t *pbuf, uint16_t length)
{
    /* USER CODE BEGIN 5 */
    printf("CDC_Control_FS cmd:0x%x, pbuf:%s, length:%d\r\n", cmd, pbuf, length);
    switch (cmd)
    {
    case CDC_SEND_ENCAPSULATED_COMMAND:

        break;

    case CDC_GET_ENCAPSULATED_RESPONSE:

        break;

    case CDC_SET_COMM_FEATURE:

        break;

    case CDC_GET_COMM_FEATURE:

        break;

    case CDC_CLEAR_COMM_FEATURE:

        break;

        /*******************************************************************************/
        /* Line Coding Structure                                                       */
        /*-----------------------------------------------------------------------------*/
        /* Offset | Field       | Size | Value  | Description                          */
        /* 0      | dwDTERate   |   4  | Number |Data terminal rate, in bits per second*/
        /* 4      | bCharFormat |   1  | Number | Stop bits                            */
        /*                                        0 - 1 Stop bit                       */
        /*                                        1 - 1.5 Stop bits                    */
        /*                                        2 - 2 Stop bits                      */
        /* 5      | bParityType |  1   | Number | Parity                               */
        /*                                        0 - None                             */
        /*                                        1 - Odd                              */
        /*                                        2 - Even                             */
        /*                                        3 - Mark                             */
        /*                                        4 - Space                            */
        /* 6      | bDataBits  |   1   | Number Data bits (5, 6, 7, 8 or 16).          */
        /*******************************************************************************/
    case CDC_SET_LINE_CODING:

        break;

    case CDC_GET_LINE_CODING:

        break;

    case CDC_SET_CONTROL_LINE_STATE:

        break;

    case CDC_SEND_BREAK:

        break;

    default:
        break;
    }

    return (USBD_OK);
    /* USER CODE END 5 */
}

/**
 * @brief  Data received over USB OUT endpoint are sent over CDC interface
 *         through this function.
 *
 *         @note
 *         This function will issue a NAK packet on any OUT packet received on
 *         USB endpoint until exiting this function. If you exit this function
 *         before transfer is complete on CDC interface (ie. using DMA controller)
 *         it will result in receiving more data while previous ones are still
 *         not sent.
 *
 * @param  Buf: Buffer of data to be received
 * @param  Len: Number of data received (in bytes)
 * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
 */
static int8_t CDC_Receive_FS(uint8_t *Buf, uint32_t *Len, uint8_t epnum)
{
    /* USER CODE BEGIN 6 */
    // printf("[%s:ep:0x%02x:len:%d]:%s\r\n", __FUNCTION__, epnum, *Len, Buf);
    CDC_Transmit_FS(Buf, *Len, 0x80 + epnum);
    USBD_CDC_SetRxBuffer(hUsbDeviceFS, &Buf[0]);
    USBD_CDC_ReceivePacket(hUsbDeviceFS, epnum);

    //	static int index = 0;
    //	char buff[32];
    //	int buff_size = 0;
    //	buff_size = snprintf(buff, sizeof(buff), "index:%d", index++);
    //	CDC_Transmit_FS((uint8_t*)buff, buff_size, 0x80+epnum);

    return (USBD_OK);
    /* USER CODE END 6 */
}

/**
 * @brief  CDC_Transmit_FS
 *         Data to send over USB IN endpoint are sent over CDC interface
 *         through this function.
 *         @note
 *
 *
 * @param  Buf: Buffer of data to be sent
 * @param  Len: Number of data to be sent (in bytes)
 * @retval USBD_OK if all operations are OK else USBD_FAIL or USBD_BUSY
 */
uint8_t CDC_Transmit_FS(uint8_t *Buf, uint16_t Len, uint8_t epnum)
{
    uint8_t result = USBD_OK;
    /* USER CODE BEGIN 7 */
    // printf("[%s:ep:0x%02x:len:%d]:%s\r\n", __FUNCTION__, epnum, Len, Buf);

    USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)hUsbDeviceFS->pClassData;
    if (hcdc->TxState != 0)
    {
        return USBD_BUSY;
    }
    // HAL_UART_Transmit_IT(&huart1,Buf,Len);

    USBD_CDC_SetTxBuffer(hUsbDeviceFS, Buf, Len);
    result = USBD_CDC_TransmitPacket(hUsbDeviceFS, epnum);
    /* USER CODE END 7 */
    return result;
}
